package net.cyndeline.rlgraph.planar.face

import scala.collection.mutable.ArrayBuffer

/**
 * Represents a single face in a planar graph, with vertices in the same order they're traversed
 * in the face.
 */
class Face[VType : Manifest](val vertices: List[VType]) {
  if (vertices.size < 2) throw new IllegalArgumentException("Face " + vertices + " contains " + vertices.size + " vertices, must have 2 or more.")

  val edges: List[(VType, VType)] = makeEdges()

  private val edgeSet = edges.toSet

  /**
   * Checks if this face contains a specified edge.
   * @param edge Edge to look for.
   * @return True if the edge is a part of this face, otherwise false.
   */
  def containsEdge(edge: (VType, VType)): Boolean = edgeSet.contains(edge)

  /**
   * Checks if this face contains at least one edge present in the other face. This is done
   * by checking the reverse of edges in this face, since every edge is traversed in opposite
   * directions when parsing them from a planar embedding.
   *
   * @param otherFace Face to check common edges for.
   * @return True if the other face shares at least one edge, otherwise false.
   */
  def isAdjacentTo(otherFace: Face[VType]): Boolean = {
    val thisEdges = edges.iterator
    while (thisEdges.hasNext) {
      val edge = thisEdges.next()
      if (otherFace.containsEdge((edge._2, edge._1))) return true
    }

    false
  }

  def vertexSize = vertices.size
  def edgeSize = edges.size

  override def toString: String = vertices.toString

  override def equals(other: Any): Boolean = other match {
    case f: Face[VType] => {
      if (f.vertices.toSet != vertices.toSet) return false
      if (this eq f) return true
      if (f.vertices == vertices) return true

      var allRotations = Set[List[VType]]()
      var current = vertices
      for (i <- 0 until vertices.size) {
        allRotations += current
        current = current.tail ::: List(current.head)
      }

      allRotations.contains(f.vertices)
    }
    case _ => false
  }

  override def hashCode = vertices.##

  private def makeEdges(): List[(VType, VType)] = {
    val first = vertices.head
    val vIt = vertices.iterator
    var previous = vIt.next()
    val result = new ArrayBuffer[(VType, VType)]()

    while(vIt.hasNext) {
      val vertex = vIt.next()
      result += ((previous, vertex))
      previous = vertex
    }

    result += ((previous, first))
    result.toList
  }

}
